/*
 * Copyright (c) 2012 Sean Connelly
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

var common = require('./common.js');

//
// interface that rest of base uses to buffer text
//

function textStream_createMarker(start, stop)
{
	return {
			start: start,
			stop: stop,
			add: function(marker) { // add marker to this marker's position
					this.start.row += marker.start.row;
					this.start.col += marker.start.col;
					this.start.pos += marker.start.pos;
					this.stop.row += marker.start.row;
					this.stop.col += marker.start.col;
					this.stop.pos += marker.start.pos;
					return this;
				},
			copy: function() { // create a copy of this marker
					return textStream_createMarker(this.start, this.stop);
				}
		};
}

function textStream_create(txt)
{
	var prev = { // one position ago
			row: 0,
			col: -1,
			pos: -1,
			ch: false
		};
	var next = { // current position
			row: 0,
			col: 0,
			pos: 0,
			ch: txt.length > 0 ? txt.charAt(0) : false
		};
	var posStack = [];
	return {
			isEOF: function() {
					return next.pos - 1 >= txt.length;
				},
			nextChar: function() { // return next character, advance pointer
					prev = common.deepCopy(next);
					next.pos++;
					next.col++;
					next.ch = txt.length > next.pos ? txt.charAt(next.pos) : false;
					if (prev.ch === '\n' || (prev.ch === '\r' && next.ch !== '\n'))
					{
						next.row++;
						next.col = 0;
					}
					return prev.ch;
				},
			nextBlock: function(count) {
					var str = '';
					while (count > 0)
					{
						var ch = this.nextChar();
						if (ch === false)
							return str;
						str += ch;
						count--;
					}
					return str;
				},
			peekChar: function(offset) { // offset is between [0, 32)
					if (offset < 0 || offset >= 32)
						throw 'offset out of range';
					if (next.pos + offset < 0 || next.pos + offset >= txt.length)
						return false;
					return txt.charAt(next.pos + offset);
				},
			peekBlock: function(len) { // len is between 1 and 32
					if (len < 1 || len > 32)
						throw 'length out of range';
					return txt.substr(next.pos, len);
				},
			skipChars: function(count) {
					while (count > 0)
					{
						this.nextChar();
						count--;
					}
					return this;
				},
			pushIncluding: function(name) { // push position, including the last character
					posStack.push(common.deepCopy(prev));
					return this;
				},
			pushExcluding: function(name) { // push position, excluding the last character
					posStack.push(common.deepCopy(next));
					return this;
				},
			popIncluding: function() { // return a marker, including the last character
					return textStream_createMarker(posStack.pop(), common.deepCopy(next));
				},
			popExcluding: function() { // return a marker, excluding the last character
					return textStream_createMarker(posStack.pop(), common.deepCopy(prev));
				},
			markChar: function() { // one character width marker
					return textStream_createMarker(common.deepCopy(prev), common.deepCopy(next));
				},
			markSpot: function() { // zero character width marker
					return textStream_createMarker(common.deepCopy(next), common.deepCopy(next));
				},
			markLastSpot: function() { // zero character width marker
					return textStream_createMarker(common.deepCopy(prev), common.deepCopy(prev));
				}
		};
}

exports.create = textStream_create;
